{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  ZMidi.Basic.Primitive.RenderMidi
-- Copyright   :  (c) Stephen Tetley 2012
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  unstable
-- Portability :  GHC
--
-- 
--
--------------------------------------------------------------------------------

module ZMidi.Basic.Primitive.RenderMidi
  ( 
   
    genFormat1
  , genFormat1_IO

  ) where


import ZMidi.Basic.Primitive.EventList

import ZMidi.Core                               -- package: zmidi-core


import Control.Applicative
import Data.Time


genFormat1 :: EventList -> MidiFile
genFormat1 evs = MidiFile { mf_header = midi_header
                          , mf_tracks = [ hdr, eventTrack evs ]}
  where
    hdr = MidiTrack [ sequence_name "Track 0", delta_end_of_track ]

-- | As per 'genFormat1' but adds a timestamp to the info track 
-- (track 0).
--
genFormat1_IO :: EventList -> IO MidiFile
genFormat1_IO evs =
   (\hdr -> MidiFile { mf_header = midi_header
                     , mf_tracks = [ hdr, eventTrack evs ]} )
    <$> io_infoTrack
   

midi_header :: MidiHeader
midi_header = MidiHeader { hdr_format    = MF1
                         , num_tracks    = 2
                         , time_division = TPB 480
                         }


io_infoTrack :: IO MidiTrack
io_infoTrack = mk <$> getZonedTime
  where
    mk ztim = MidiTrack [ generic_text "Generated by ZMidi."
                        , generic_text $ midiTimeStamp ztim
                        , sequence_name "Track 0"
                        , delta_end_of_track
                        ]



generic_text :: String -> MidiMessage
generic_text ss = (0, MetaEvent $ TextEvent GENERIC_TEXT ss)

sequence_name :: String -> MidiMessage
sequence_name ss = (0, MetaEvent $ TextEvent SEQUENCE_NAME ss)


delta_end_of_track :: MidiMessage
delta_end_of_track = (0, MetaEvent $ EndOfTrack)

set_tempo :: MidiMessage
set_tempo = (0, MetaEvent $ SetTempo 500000)



eventTrack :: EventList -> MidiTrack
eventTrack evts = MidiTrack $ evt0 : evt1 : toList evts
  where
    evt0 = sequence_name "Track 1"
    evt1 = set_tempo




--------------------------------------------------------------------------------


-- | To be used with getZonedTime

midiTimeStamp :: ZonedTime -> String

midiTimeStamp zt = bodyS [] 
  where
    bodyS       = localTimeS . showChar ' ' . localDayS
    local_tim   = zonedTimeToLocalTime zt
    localTimeS  = timeOfDay  $ localTimeOfDay $ local_tim
    localDayS   = showString $ showGregorian  $ localDay local_tim

timeOfDay :: TimeOfDay -> ShowS
timeOfDay t = 
    fn todHour . showChar ':' . fn todMin . showChar ':' . fn (floori . todSec)
  where
    fn f = pad2 (f t) 


pad2 :: Int -> ShowS
pad2 i | i < 10    = ('0':) . shows i
       | otherwise = shows i  


floori :: RealFrac a => a -> Int
floori = floor